# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         feishu_client.py
# Description:  飞书客户端
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2020/8/3 11:05
# -------------------------------------------------------------------------------
import base64

import hashlib
import hmac
import json
import logging
import queue
import re
import time
from json.decoder import JSONDecodeError
from urllib.parse import quote_plus
import requests

logger = logging.getLogger(__name__)


class FeiShuClient(object):
    """
    飞书客户端
    开发者文档：https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN
    """

    def __init__(self, webhook=None, secret=None, fail_notice=False):
        """
        初始化飞书客户端
        Args:
            webhook: 飞书webhook
            secret: 飞书secret
            fail_notice: 消息发送失败提醒，默认为False不提醒
        """
        self.headers = {'Content-Type': 'application/json; charset=utf-8'}
        self.queue = queue.Queue(20)  # 飞书官方限流每分钟发送20条信息
        self.webhook = webhook
        self.secret = secret
        self.fail_notice = fail_notice
        self.start_time = time.time()  # 加签时，请求时间戳与请求时间不能超过1小时，用于定时更新签名
        self.timestamp = ""
        self.sign = ""
        if self.secret:
            self.get_sign()

    def get_sign(self):
        """
        飞书群自定义机器人安全设置加签时，签名中的时间戳与请求时不能超过一个小时，所以每个1小时需要更新签名
        Returns:

        """
        self.timestamp = str(round(self.start_time))
        string_to_sign = '{}\n{}'.format(self.timestamp, self.secret)
        hmac_code = hmac.new(string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
        self.sign = base64.b64encode(hmac_code).decode('utf-8')

    def post(self, data):
        """
        发送消息（内容UTF-8编码）
        Args:
            data: 消息数据（字典）

        Returns: 返回消息发送结果

        """
        now = time.time()

        # 飞书自定义机器人安全设置加签时，签名中的时间戳与请求时不能超过一个小时，所以每个1小时需要更新签名
        if now - self.start_time >= 3600 and self.secret is not None:
            self.start_time = now
            self.get_sign()

        # 飞书自定义机器人现在每分钟最多发送20条消息
        self.queue.put(now)
        if self.queue.full():
            elapse_time = now - self.queue.get()
            if elapse_time < 60:
                sleep_time = int(60 - elapse_time) + 1
                logger.info('飞书官方限制机器人每分钟最多发送20条，当前发送频率已达限制条件，休眠 {} s'.format(sleep_time))
                time.sleep(sleep_time)

        try:
            response = requests.post(self.webhook, headers=self.headers, json=data)
        except requests.exceptions.HTTPError as exc:
            logger.error("消息发送失败，HTTP error: %d，reason: %s" % (exc.response.status_code, exc.response.reason))
            raise
        except requests.exceptions.ConnectionError:
            logger.error("消息发送失败，HTTP connection error!")
            raise
        except requests.exceptions.Timeout:
            logger.error("消息发送失败，Timeout error!")
            raise
        except requests.exceptions.RequestException:
            logger.error("消息发送失败, Request Exception!")
            raise
        else:
            try:
                result = response.json()
            except JSONDecodeError:
                logger.error("服务器响应异常，状态码：{status_code}，响应内容：{text}".format(status_code=response.status_code,
                                                                            text=response.text))
                return {'errcode': 500, 'errmsg': '服务器响应异常'}
            else:
                logger.info('飞书机器人发送消息结果：{}'.format(result))
                # 消息发送失败提醒（errcode 不为 0，表示消息发送异常）
                if self.fail_notice and result.get('errcode', True):
                    time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
                    error_data = {
                        'timestamp': self.timestamp,
                        'sign': self.sign,
                        "msg_type": "text",
                        "content": {
                            "text": "[注意-自动通知]飞书机器人消息发送失败，时间：%s，原因：%s，请及时跟进，谢谢!" % (
                                time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常')
                        },
                    }
                    logger.error("消息发送失败，自动通知：%s" % error_data)
                    requests.post(self.webhook, headers=self.headers, json=error_data)
            return result

    def send_text(self, text):
        """
        发送文本类型消息
        Args:
            text: 消息内容

        Returns: 返回消息发送结果

        """
        data = {'timestamp': self.timestamp, 'sign': self.sign, 'msg_type': 'text'}
        if text:
            data['content'] = {'text': text}
        else:
            logger.error('发送文本类型消息时，消息内容不能为空！！！')
            raise ValueError('发送文本类型消息时，消息内容不能为空！！！')

        logger.info('发送文本消息：{}'.format(data))
        return self.post(data)

    def send_card(self, content):
        """
        发送卡片类型消息
        Args:
            content: 卡片类型消息的内容

        Returns: 返回消息发送结果

        """
        data = {'timestamp': self.timestamp, 'sign': self.sign, 'msg_type': 'interactive'}
        if content:
            data['card'] = content
            logger.info('发送卡片类型消息：{}'.format(data))
            self.post(data)
        else:
            logger.error('发送卡片类型消息时，消息内容不能为空！！！')
            raise ValueError('发送卡片类型消息时，消内容不能为空！！！')


if __name__ == '__main__':
    feishu_client = FeiShuClient(
        webhook='https://open.feishu.cn/open-apis/bot/v2/hook/98837df5-f5d0-4574-8103-eb3d76fbeec1',
        secret='9gegqGJbvXliIUgMl1khng')
    # time.sleep(10)
    # print(res.send_text('你好'))
    # print(str(round(time.time() * 1000)))

    data = {
        "config": {
        },
        "header": {
            "title": {
                "content": "场景测试报告",
                "tag": "plain_text"
            },
            "template": "blue"
        },
        "elements": [
            {
                "tag": "div",
                "text": {
                    "tag": "lark_md",
                    "content": "成功率：**100.0 %**\n场景用例总数：**4**\n成功用例数：**4**\n失败用例数：**4**\n测试执行人：超级管理员\n执行总时长：00:00:01.307\n执行时间：2022-08-17 09:35:38"
                },
            },
            {
                "tag": "div",
                "text": {
                    "tag": "lark_md",
                    "content": "[点击查看测试报告](https://open.feishu.cn/)"
                }
            }
        ],
    }
    res = feishu_client.send_card(data)
    print(res)
